好的好的,經過了前幾篇文章之後,想必大家應該對 Coroutine 有一些了解了吧,我在這邊快速複習一下。
Coroutine 是用來解決非同步程式的執行問題,使用它,你就可以避免使用 Callback,也就不會發生 回調地獄(Callback hell)、控制權轉移(Inversion of control)的情況。它在官網是以輕量的執行緒介紹。同時建立相同數量的 Coroutine 與執行緒,就可以立刻發現兩者的執行速度差異。
接下來要提到的是,它是屬於無棧協程,也就是說,在 Coroutine 內部不像執行緒一樣使用調用棧 (call stack) 用來儲存 Coroutine 的上下文,在 Coroutine 的內部是使用 Callback 的機制在進行非同步處理,只不過 Coroutine 把這層包起來了。所以我們感覺不出來是用 Callback 機制。
另外, 在 Coroutine 中,我們將耗時、延時的任務定義在 suspend 函式中,而這個 suspend 函式必須要在 Coroutine scope 中執行,無法在 Coroutine scope 以外呼叫這個 suspend 函式,原因無他,就是因為在 suspend 函式裡面有包含了 Continuation 的實例,而這個實例包含著 Coroutine 上下文,當完成 suspend 函式之後,就可以調用 Continuation 中的 resumeWith
來把 Coroutine 切換回來。這就是我說的 Callback 機制。
根據前面的描述,Kotlin 的 Coroutine 必須要在一個稱作 Coroutine Scope 的範圍內才能運作。除了需要一個 Coroutine Scope 以外,在這個範圍內,如果需要執行一個非同步的程式, Kotlin 提供了一個關鍵字 suspend
用來定義這個函式是一個需要暫停的函式。最後,我們知道 Coroutine 其實也是運行在執行緒中,那麼我們也可以依照我們的需求切換執行緒,在 Kotlin 的 Coroutine 是使用調度器 (Dispatchers) 來切換不同的執行緒。
有三種建立 CoroutineScope 的方式
Job
,我們可以在 launch()
中直接執行任務,或是透過回傳的 Job
在適當的時機呼叫其 start()
或者 join()
延遲執行。適合使用在沒有回傳值的非同步任務。async()
來建立 Coroutine Scope, async()
的回傳值將會回傳一個 Deferred<T>
,當任務完成時,我們可以使用 await()
來取得回傳值得內容。Kotlin 的 coroutine 使用 suspend
關鍵字用來把函式定義成可暫停的 (suspendable)。
suspend fun suspendFun() = coroutineScope {
doSomething()
}
如上,在 suspendFun()
前方加上 suspend
就會讓這個函式變成可暫停的函式,也就可以在 Coroutine scope 中使用,並在調用時暫停,完成任務時在原地恢復。
在 suspend 函式中,我們除了可以調用其他的 suspend 函式,當然也可以調用普通的函式。
Coroutine 定義了幾個 top-level 的 suspend function 供我們使用,如下:
delay()
:暫停 Coroutine ,與執行緒的 Thread.sleep()
不同,不會停止執行緒。yield()
:把目前 Coroutine 調度器執行的任務暫停並讓給其他的任務來執行。withContext()
:在當下的 Coroutine scope 中,使用不同的 context。withTimeout()
:設定執行時間,如果時間到還沒有完成,就會拋出例外。withTimeoutOrNull()
:同上,只不過是回傳一個 Null 值。awaitAll()
:等待所有的 Deferred
完成,或是其中一個發生錯誤。joinAll()
:暫停 Coroutine 直到所有的 Job
都完成。等同於 jobs.forEach { it.join() }.
調度器是用來決定該 Coroutine 要在哪個執行緒來執行。如果沒有設定,將會使用 Dispatchers.Default。
有四種 Dispatchers,如下:
簡單來說:Dispatchers.Default 是用在背景運算的 coroutine ,而 Dispatchers.Main 是用在畫面更新上(主執行緒)。
Coroutine 是由三個要素所組成,CoroutineScope、suspend fun 以及 Dispatchers。suspend 函式 只能在 CoroutineScope 中執行,根據函式是否有回傳值,我們可以選擇使用 launch()
或是 async()
來建立一個 coroutineScope,前者是沒有回傳值的,後者則是有一個回傳值的。
除了我們自己定義的 suspend 函式以外,Coroutine 也有定義了一些 top-level suspend 函式。如 delay()
。
最後,每一個 CoroutineScope 都可以帶入 CoroutineContext,如果沒有帶入,那麼就會使用預設的值,預設的 CoroutineContext 是 Dispatchers.Default。
有興趣的讀者歡迎參考:https://coroutine.kotlin.tips/
天瓏書局